package com.dospyer.jsql.trx;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author : peigen
 * Date: 2021/10/22 5:15 下午
 */
public class TransactionManager {

    /**
     * 单例模式
     */
    private static volatile TransactionManager transactionManager;

    private TransactionManager() {
    }

    /**
     * DCL 单例
     *
     * @return
     */
    public static TransactionManager getInstance() {
        if (transactionManager == null) {
            synchronized (TransactionManager.class) {
                if (transactionManager == null) {
                    transactionManager = new TransactionManager();
                }
            }
        }
        return transactionManager;
    }

    /**
     * 全局递增事务ID
     */
    private Long trxId = 0L;

    /**
     * 保存系统中活跃的事务id
     */
    private List<Long> activeTrxList = new ArrayList();

    /**
     * 保存当前线程的事务id
     */
    private ThreadLocal<Long> currentTrxId = new ThreadLocal<>();

    /**
     * 保存当前事务的 ReadView
     */
    private ThreadLocal<ReadView> currentReadView = new ThreadLocal<>();

    /**
     * 开始事务
     *
     * @return 返回事务id
     */
    public synchronized Long begin() {
        long current = ++trxId;
        activeTrxList.add(current);
        currentTrxId.set(current);
        return current;
    }

    /**
     * 结束事务
     */
    public synchronized void commit() {
        activeTrxList.remove(currentTrxId.get());
        currentTrxId.remove();
        currentReadView.remove();
    }

    /**
     * 获取事务id
     *
     * @return
     */
    public Long getTrxId() {
        return currentTrxId.get();
    }

    /**
     * 获取 ReadView
     *
     * @return
     */
    public ReadView getReadView() {
        return currentReadView.get();
    }

    /**
     * 获取当前事务的 ReadView（一致性视图），执行事务首句 sql 时需要执行此方法。
     * <p>
     * 如果当前系统 存在 1,2,3,4 这三个事务，事务2和事务3尚未提交，事务1和事务4提交了。
     * 这个时候新开启事务生成的 ReadView 活跃事务列表包含2、3，低水位是2，高水位是4
     *
     * @return
     */
    public synchronized ReadView assignReadView() {
        // 获取当前活跃的事务列表
        List<Long> activeTrxIds = new ArrayList<>(activeTrxList);
        Collections.sort(activeTrxIds);

        ReadView readView = new ReadView();
        readView.setCreatorTrxId(currentTrxId.get());
        // 列表中最小的事务id就是低水位
        if (activeTrxIds == null || activeTrxIds.size() == 0) {
            // 如果没有活跃的事务，当前事务id就是低水位
            readView.setMinTrxId(readView.getCreatorTrxId());
        } else {
            readView.setMinTrxId(activeTrxIds.get(0));
        }

        // 当前系统下一个要分配的事务id，注意不是活跃事务列表中最大值
        readView.setMaxTrxId(trxId + 1);
        readView.setMIds(activeTrxIds);

        // 保存 ReadView
        currentReadView.set(readView);
        return readView;
    }


}
